package com.xiaoshuidi.cloud.module.report.xxljob;

import com.xiaoshuidi.cloud.framework.tenant.core.aop.TenantIgnore;
import com.xiaoshuidi.cloud.module.report.constant.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.report.constant.PublicUseConstant;
import com.xiaoshuidi.cloud.module.report.convert.finance.CollectInAdvanceConvert;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractOrderFeesDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractRoomDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractSettlementDetailDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.report.*;
import com.xiaoshuidi.cloud.module.report.dal.mapper.*;
import com.xiaoshuidi.cloud.module.report.dal.mapper.report.IncomeDetailDayReportMapper;
import com.xiaoshuidi.cloud.module.report.service.contract.ContractOperateService;
import com.xiaoshuidi.cloud.module.report.service.finance.CollectInAdvanceService;
import com.xiaoshuidi.cloud.module.report.service.finance.IncomeDetailReportService;
import com.xiaoshuidi.cloud.module.report.utils.DateUtils;
import com.xiaoshuidi.cloud.module.report.utils.ThreadPoolUtils;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
 * 预收报表 job 每日4点跑一次
 *
 */
@Component
@Slf4j
public class CollectInAdvanceHandleJob {
    @Resource
    private CollectInAdvanceService collectInAdvanceService;
    @Resource
    private ContractOperateService contractOperateService;
    @Resource
    private ContractRentorderMapper contractRentorderMapper;
    @Resource
    private ContractOrderfeesMapper contractOrderfeesMapper;
    @Resource
    private ContractRoomMapper contractRoomMapper;
    @Resource
    private IncomeDetailReportService incomeDetailReportService;
    @Resource
    private IncomeDetailDayReportMapper incomeDetailDayReportMapper;
    @Resource
    private ContractSettlementMapper contractSettlementMapper;
    @Resource
    private ContractSettlementDetailMapper contractSettlementDetailMapper;

    @XxlJob("collectInAdvanceHandleJob")
    @TenantIgnore
    public void execute() {
        Integer minusDay = 1;
        String params = XxlJobHelper.getJobParam();
        if(StringUtils.isNotBlank(params)){
            try {
                minusDay = Integer.parseInt(params);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        //获取当月需要统计预收报表的合同
        //获取当月存在权责的合同
        LocalDate yesterday = LocalDate.now().minusDays(minusDay);
        final Integer minusDays = minusDay;
        long totalNum = contractOperateService.countHaveIncomeContractsYesterday(yesterday);
//        totalNum = 1L;
        if (totalNum <= 1000) {
            //合同数<500直接一次处理
            List<ReportContractInfoDO> list = contractOperateService.selectHaveIncomeContractsYesterday(yesterday);
            handlerAdvance(list,minusDays);
//            ThreadPoolUtils.poolExecutor.execute(() -> handlerAdvance(list));
        } else {
            //分500一批处理
            int pageNo = 0;
            int pageSize = 1000;
            List<ReportContractInfoDO> list ;
            int num = (int)totalNum/pageSize + 1 ;
            CountDownLatch latch = new CountDownLatch(num);
            do {
                int from = pageNo * pageSize;
                list = contractOperateService.selectHaveIncomeContractsYesterdayInTurn(yesterday, from, pageSize);
                final List<ReportContractInfoDO> finalList = list;
                pageNo++;
//                handlerAdvance(finalList,minusDays);
                ThreadPoolUtils.poolExecutor.execute(() -> {
                    handlerAdvance(finalList,minusDays);
                    latch.countDown();
                });
            } while (list.size() == 1000);
            try {
                log.info("---------------处理预收数据完成-------------");
                latch.await();
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 处理预收明细项
     */
    public void handlerAdvance(List<ReportContractInfoDO> infoDOList,int minusDays){
        if(CollectionUtils.isEmpty(infoDOList)){
            return;
        }
        final String yesterdayMonth = DateUtils.getYesterdayMonthAnI(minusDays);
        LocalDateTime endTime = LocalDateTime.now().minusDays(minusDays).toLocalDate().atTime(23,59);
        LocalDateTime startTime = endTime.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate().atTime(0,0);
        List<CollectInAdvanceDO> advanceDOList = new ArrayList<>();
        for (ReportContractInfoDO infoDO : infoDOList) {
            //上月期末余额是本月期初余额  只统计算租金的
            CollectInAdvanceDO collectInAdvanceDO = collectInAdvanceService.selectRentAdvanceByContractIdAndMonth(infoDO.getContractId());
            //本月结转=本月实际发生的权责房租、能耗、一次性费用的累计
            IncomeDetailReportDO incomeDetail = incomeDetailReportService.selectDetailsByContractIdInCurrentMonth(infoDO.getContractId(),yesterdayMonth);
            CollectInAdvanceDO advanceDO = CollectInAdvanceConvert.INSTANCE.converDetail2Advance(incomeDetail);
            if(advanceDO==null){
                continue;
            }
            advanceDO.setActualRefundAmount(BigDecimal.ZERO);
            advanceDO.setId(null);
            /**
             *   期初余额：上月期末余额||本月租金收入
             *   本月新增：各种账单实收金额
             *   本月结转：累计权责收入
             *   期末余额：上月期末余额 +本月新增 -本月结转 || 各种账单实收金额+预充值 -本月结转
             */
            //处理在租状态
            if(ContractStatusEnum.ACTIVE.getStatus().equals(infoDO.getContractStatus())||
                    ContractStatusEnum.CHECK_IN.getStatus().equals(infoDO.getContractStatus())){
                advanceDO.setRentStatus(PublicUseConstant.RENT_IN);
            }else if (ContractStatusEnum.NORMAL_END.getStatus().equals(infoDO.getContractStatus())||
                    ContractStatusEnum.ABNORMAL_END.getStatus().equals(infoDO.getContractStatus())){
                advanceDO.setRentStatus(PublicUseConstant.RENT_OUT);
            }
            if(collectInAdvanceDO ==null){
                /**
                 * 没有预收数据
                 * 上月没有期初余额
                 * 1、系统上线之初  初始化数据  所有的合同账单支付金额-已发生租金权责-已发生水电权责
                 * 2、补录合同  同上
                 *  上面两个   合同创建时间在合同开始时间之后
                 * 3、正常合同第一个月的预收报表统计(此时直接当期初余额为0)
                 */
                if(infoDO.getCreateTime().isAfter(infoDO.getStartTime().atTime(23,59))){
                    //查询所有已支付账单和已充值金额  本月支付的不算在内，本月支付会在本月新增里
                    BigDecimal sumRent = contractRentorderMapper.sumAllAmountByContractId(infoDO.getContractId(),startTime);
                    //预充值的 因为企业合同是大钱包，无法拆分，后期单独做个预充值报表
//                    BigDecimal sumPre = financeMoneyFlowMapper.sumTotalPreRecharge(infoDO.getContractNo());
//                    sumPre = sumPre==null?BigDecimal.ZERO:sumPre;
                    sumRent = sumRent==null?BigDecimal.ZERO:sumRent;
                    //查询合同的租金权责
                    BigDecimal sumPower = incomeDetailReportService.sumTotalRentAndWaterByContractId(infoDO.getContractId());
                    sumPower = sumPower==null?BigDecimal.ZERO:sumPower;
                    advanceDO.setOpeningBalance(sumRent.subtract(sumPower));
                }else {
                    advanceDO.setOpeningBalance(BigDecimal.ZERO);
                }
                /**
                 * 本月结转 如果不是补录合同 直接就是本月权责
                 * 补录合同 需去除之前月份的权责，只统计本月的
                 */
                if(incomeDetail.getAdditionalFlag()){
                    handleAdditionalCarry(advanceDO,minusDays);
                    //补录合同的期初余额不算本月结转 多减了 需要补回来
                    advanceDO.setOpeningBalance(advanceDO.getOpeningBalance().add(advanceDO.getCurCarryFoward()));
                }else {
                    advanceDO.setCurCarryFoward(incomeDetail.getCurRentReduce().add(incomeDetail.getCurOtherReduce()));
                    //如果是本月的补录合同 也需要将本月结转补上
                    ContractRoomDO contractRoomDO = contractRoomMapper.selectById(advanceDO.getContractId());
                    if(contractRoomDO.getStartTime().atTime(23,59).isBefore(contractRoomDO.getCreateTime())){
                        advanceDO.setOpeningBalance(advanceDO.getOpeningBalance().add(advanceDO.getCurCarryFoward()));
                    }
                }
                BigDecimal couponAmount = contractRentorderMapper.sumAllCouponByContractId(infoDO.getContractId(),startTime);
                advanceDO.setCouponAmount(couponAmount);
            }else {
                /**
                 * 有预收数据  需判断是上月还是本月的
                 * 预收是上月 代表本月的权责不可能是补录 本月结转直接取本月权责的数据
                 * 预收是本月 上月有预收和没预收两种情况  本月结转不一样
                 */
                if(collectInAdvanceDO.getStatisticsMonth().equals(yesterdayMonth)){
                    advanceDO.setOpeningBalance(collectInAdvanceDO.getOpeningBalance());
                    if(collectInAdvanceService.selectByContractId(advanceDO.getContractId()).size()>1){
                        //上月有预收
                        advanceDO.setCurCarryFoward(incomeDetail.getCurRentReduce().add(incomeDetail.getCurOtherReduce()));
                    }else {
                        handleAdditionalCarry(advanceDO,minusDays);
                    }

                }else {
                    advanceDO.setOpeningBalance(collectInAdvanceDO.getEndingBalance());
                    advanceDO.setCurCarryFoward(incomeDetail.getCurRentReduce().add(incomeDetail.getCurOtherReduce()));
                }
            }
            //本月新增 本月账单费用
            BigDecimal curAmount = contractRentorderMapper.sumAmountByContractIdAndTime(infoDO.getContractId(), startTime, endTime);
            BigDecimal curMonthCouponAmount = contractRentorderMapper.sumCouponByContractIdAndTime(infoDO.getContractId(), startTime, endTime);
            curAmount = curAmount==null?BigDecimal.ZERO:curAmount;
            curMonthCouponAmount = curMonthCouponAmount==null?BigDecimal.ZERO:curMonthCouponAmount;
            advanceDO.setCurNewAdd(curAmount);
            if(advanceDO.getCouponAmount()==null){
                advanceDO.setCouponAmount(BigDecimal.ZERO);
            }
            advanceDO.setCouponAmount(advanceDO.getCouponAmount().add(curMonthCouponAmount));
            List<Long> settleIds = contractSettlementMapper.queryByContractIdOnPayedInPayTime(infoDO.getContractId(), startTime, endTime);
            if (CollectionUtils.isNotEmpty(settleIds)) {
                //结算单费用
                List<ContractSettlementDetailDO> settlementDetailList = contractSettlementDetailMapper.selectByOrderIds(settleIds);
                BigDecimal actualRefundAmountOut = settlementDetailList.stream().filter(e->"out".equals(e.getFlowType())&&2!=e.getFeeId()).map(ContractSettlementDetailDO::getRefundableAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal actualRefundAmountIn = settlementDetailList.stream().filter(e->"in".equals(e.getFlowType())&&2!=e.getFeeId()).map(ContractSettlementDetailDO::getRefundableAmount).reduce(BigDecimal.ZERO, BigDecimal::add).negate();
                advanceDO.setActualRefundAmount(actualRefundAmountOut.add(actualRefundAmountIn));
            }
            //期末余额
            advanceDO.setEndingBalance(advanceDO.getOpeningBalance().add(advanceDO.getCurNewAdd()).subtract(advanceDO.getCurCarryFoward())
                    .subtract(advanceDO.getActualRefundAmount()).add(advanceDO.getCouponAmount()));
            advanceDOList.add(advanceDO);
        }
        //查询当月是否已经生成过预收明细收入
        List<Long> contractIds = advanceDOList.stream().map(CollectInAdvanceDO::getContractId).collect(Collectors.toList());
        List<CollectInAdvanceDO> inAdvanceDOList = collectInAdvanceService.selectDetailsInCurrentMonth(DateUtils.getYesterdayMonthAnI(minusDays),contractIds);
        if (CollectionUtils.isEmpty(inAdvanceDOList)) {
            collectInAdvanceService.batchInsert(advanceDOList);
        }else {
            //不为空则修改
            advanceDOList.forEach(e->{
                inAdvanceDOList.forEach(report->{
                    if(e.getContractId().equals(report.getContractId())){
                        e.setId(report.getId());
                    }
                });
            });
            List<CollectInAdvanceDO> updateList = advanceDOList.stream().filter(e -> e.getId() != null).collect(Collectors.toList());
            List<CollectInAdvanceDO> insertList = advanceDOList.stream().filter(e -> e.getId() == null).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(updateList)) {
                collectInAdvanceService.batchUpdate(updateList);
            }
            if (CollectionUtils.isNotEmpty(insertList)) {
                collectInAdvanceService.batchInsert(insertList);
            }
        }
    }

    private void handleAdditionalCarry(CollectInAdvanceDO advanceDO,int minusDays){
        /**
         * 只需根据收入日明细是否是大于1笔，如果是，则第二笔肯定没有之前累计的，则只需获取日租金的租金+其他收入即可
         * 如果是只有1笔，则有可能是补录的第一笔，需要单独算这一天的日结转
         */
        List<IncomeDetailDayReportDO> incomeDetailDayReportDOS = incomeDetailDayReportMapper.selectByContractId(advanceDO.getContractId());
        if(incomeDetailDayReportDOS.size()>=2){
            advanceDO.setCurCarryFoward(incomeDetailDayReportDOS.get(0).getCurRentReduce().add(incomeDetailDayReportDOS.get(0).getCurOtherReduce()));
            return;
        }
        //TODO 补录场景 计算日租金此时还有问题

        //查询合同日租金
        List<ReportContractDayRentDO> dayRentList = contractOperateService.selectListByContractId(advanceDO.getContractId());
        //获取昨天所在月份
        LocalDate yesterday = LocalDate.now().minusDays(minusDays);
        int yesterdayLength = yesterday.lengthOfMonth();
        int lastMonthLength = yesterday.minusMonths(1).lengthOfMonth();
        ReportContractDayRentDO yesterdayMonth = new ReportContractDayRentDO();
        ReportContractDayRentDO lastDayMonth = new ReportContractDayRentDO();
        for (ReportContractDayRentDO dayRentDO : dayRentList) {
            if (yesterdayLength == dayRentDO.getMonthDays()) {
                yesterdayMonth = dayRentDO;
            }
            if (lastMonthLength == dayRentDO.getMonthDays()) {
                lastDayMonth = dayRentDO;
            }
        }
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(advanceDO.getContractId());
        if(contractRoomDO.getActualEndTime()!=null&&yesterday.isAfter(contractRoomDO.getActualEndTime())){
            yesterday = contractRoomDO.getActualEndTime();
        }
        //获取开始日期所在的天数
        int daysOfStartTime = contractRoomDO.getStartTime().getDayOfMonth();
        //获取昨天所在天数
        int daysOfYesterday = yesterday.getDayOfMonth();

        BigDecimal rentReduce = BigDecimal.ZERO;
        /**
         * 日租金需分段算  7.15~8.14
         * 8月10号 只需算8月1号到10号的日租金
         * 8月16号 需算8月1号到8月14号  8月15号到8月16号
         */
        //根据合同所在天数计算合同日租金和优惠金额
        if(daysOfStartTime>daysOfYesterday){
            // 8月10号： 7月份的日租金*10
            rentReduce = (lastDayMonth.getNormalDayRent().subtract(lastDayMonth.getNormalDayDiscount())).multiply(BigDecimal.valueOf(daysOfYesterday));
        }else if(daysOfStartTime==daysOfYesterday){
            //8月14号:
            rentReduce = (lastDayMonth.getNormalDayRent().subtract(lastDayMonth.getNormalDayDiscount())).multiply(BigDecimal.valueOf(daysOfYesterday-1)).add(lastDayMonth.getDayRentBalance()).subtract(lastDayMonth.getDayDiscountBalance());
        }else{
            BigDecimal lastMonthRent =  (lastDayMonth.getNormalDayRent().subtract(lastDayMonth.getNormalDayDiscount())).multiply(BigDecimal.valueOf(daysOfStartTime-1)).add(lastDayMonth.getDayRentBalance()).subtract(lastDayMonth.getDayDiscountBalance());
            rentReduce =lastMonthRent.add(yesterdayMonth.getNormalDayRent().subtract(yesterdayMonth.getNormalDayDiscount()).multiply(BigDecimal.valueOf(daysOfYesterday-daysOfStartTime)));
        }
        //计算其他收入
        LocalDate start = yesterday.with(TemporalAdjusters.firstDayOfMonth());
        List<Long> orderIds = contractRentorderMapper.queryContractIdsCurrentMonthPayed(advanceDO.getContractId(),start.atTime(0,0),yesterday.atTime(23,59));
        if(CollectionUtils.isNotEmpty(orderIds)){
            //根据账单id查询账单明细
            List<ContractOrderFeesDO> orderFeesList = contractOrderfeesMapper.selectByOrderIds(orderIds);
            //计算其他收入和折扣  排除租金 押金 物业费
            List<ContractOrderFeesDO> collect = orderFeesList.stream().filter(e -> e.getFeeId() != 1 && e.getFeeId() != 2 && e.getFeeId() !=10).collect(Collectors.toList());
            BigDecimal otherAmount = collect.stream().map(ContractOrderFeesDO::getAmount).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal otherDiscountAmount = collect.stream().map(ContractOrderFeesDO::getDiscountAmount).reduce(BigDecimal.ZERO,BigDecimal::add);
            advanceDO.setCurCarryFoward(rentReduce.add(otherAmount).subtract(otherDiscountAmount));
        }else {
            advanceDO.setCurCarryFoward(rentReduce);
        }
    }

}